/************************************************************************/
/*                                                                      */
/*			Precise 1 Second Timebase			*/
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/*                      danni@specs.de                                  */
/*                                                                      */
/************************************************************************/

#define _TIMEBASE_C_

#include <avr/io.h>
#include <avr/interrupt.h>

#include "config.h"
#include "typedefs.h"
#include "timebase.h"


#define DEBOUNCE	250L		// debounce clock (250Hz = 4msec)


uchar prescaler;
//uchar volatile second;			// count seconds

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
    uchar tcnt1h = TCNT1H;
//	PORTD ^= (1<<PD3); for measuring timer frequency

    OCR1A += XTAL / DEBOUNCE;		// new compare value
    systemTime.uiTick++;
    if (systemTime.uiTick > 0x7FFF) systemTime.uiTick = 0;

    if( ++prescaler == (uchar)DEBOUNCE )
    {
        prescaler = 0;
//        time.ulSeconds++;
//        time.second++;				// exact one second over
#if XTAL % DEBOUNCE			// handle remainder
        OCR1A += XTAL % DEBOUNCE; 		// compare once per second
#endif
    }

    TCNT1H = tcnt1h;			// restore for delay() !
}

ISR (TIMER1_CAPT_vect){
	systemTime.ulSeconds++;
	systemTime.second++;
	if (systemTime.second >= 60)
	{
		systemTime.minute++;
		systemTime.second = 0;
		if (systemTime.minute >= 60)
		{
			systemTime.hour++;
			systemTime.minute = 0;
			if (systemTime.hour >= 24)
			{
				unsigned char dayWrap = 32;
				systemDate.ucDay++;
				systemTime.hour = 0;
				switch (systemDate.ucMonth)
				{
				/*case 1:
				case 3:
				case 5:
				case 7:
				case 8:
				case 10:
				case 12:
					dayWrap = 32;
					break;
	*/
				case 4:
				case 6:
				case 9:
				case 11:
					dayWrap = 31;
					break;

				case 2:
					if (systemDate.ucYear%4 == 0) dayWrap = 30;
					else dayWrap = 29;
					break;
				}
				if (systemDate.ucDay >= dayWrap)
				{
					systemDate.ucMonth++;
					systemDate.ucDay = 1;
					if (systemDate.ucMonth >= 13)
					{
						systemDate.ucYear++;
						systemDate.ucMonth = 1;
					}
				}
			}
		}
	}
}



// 
void timer_background(void)
{

}

void init_timer( void )
{
    TCCR1B = (1<<CS10) | (1<<ICES1);			// divide by 1
    OCR1A = 0;
    TCNT1 = -1;
    second = 0;
    prescaler = 0;

    TIMSK1 = (1<<OCIE1A) | (1<<ICIE1);
}

void timer_freeze(t_time *local)
{
    local->ulSeconds = systemTime.ulSeconds;
    local->second = systemTime.second;
    local->minute = systemTime.minute;
    local->hour = systemTime.hour;
}

unsigned int timer_freeze_ticks(void)
{
    return systemTime.uiTick;
}

unsigned char timer_timeover(t_time *local, unsigned long seconds)
{
    unsigned long tempSec = 0;
    tempSec = local->ulSeconds+seconds;
    if (systemTime.ulSeconds >= tempSec) return 1;
    return 0;
}


unsigned char timer_timeover_ticks (unsigned int start, unsigned int diff)
{
    if ((systemTime.uiTick - start) >= diff) return(1);
    else return(0);
}
/*
unsigned char timer_timeover_ticks(unsigned int uiTicks, unsigned int uiDelay)
{
    if ((uiTicks + uiDelay) > 0x7FFF)
    {
        unsigned int uiTemp = uiTicks + uiDelay;
        uiTemp -= 0x8000;
        if (time.uiTick >= uiTemp && time.uiTick < uiTicks)
        {
            return 1;
        }
    }
    if (time.uiTick >= (uiTicks + uiDelay))
    {
        return 1;
    }
    return 0;
}*/

// Works every day
// return 1 if alarm is over
unsigned char timer_checkAlarm(t_time *local)
{
    if (systemTime.hour > local->hour) return 1;
    else if(systemTime.minute > local->minute) return 1;
    else if(systemTime.second >= local->second) return 1;
    else return 0;
    return 0;
}

void timer_setTimer(t_time *local, unsigned long seconds)
{
//  unsigned char ucTemp = 0;
    unsigned char ucSecond = seconds%60UL;
    unsigned long ulMinute = seconds/60UL;
    unsigned long ulHour = seconds/3600UL;
    if (ulMinute == 0 && ulHour == 0)
    {
        if (systemTime.second+ucSecond < 60)
        {
            local->second = systemTime.second+ucSecond;
            local->minute = systemTime.minute;
            local->hour = systemTime.hour;
        }
        else
        {
            if (systemTime.minute < 59)
            {
                local->hour = systemTime.hour;
                local->minute = (systemTime.minute + 1);
            }
            else
            {
                local->hour = systemTime.hour + 1;
                local->minute = 0;
            }
            local->second = (59-systemTime.second);
        }
    }
    else
    {
//    if ()
    }
}

unsigned char dateModify(t_date *date, unsigned char type, signed char modifier){
  // changes date
  // type (0 - day, 1 - months, 2 - year)
  // modifier (change value, e.g. +13 days, -2 months, ..)
  switch (type){
    case 2: // year
	  modifier = modifier % 100;
      date->ucYear += modifier;
	  date->ucYear = date->ucYear % 100;
	  
      break;

    case 1: // month
      modifier = modifier % 12;
      date->ucMonth += modifier;
      date->ucMonth = date->ucMonth % 12;
	  if (date->ucMonth == 0) date->ucMonth = 12;

      break;

    case 0: // day
    {
      unsigned char ucDaysOfMonth = 31;
      switch (date->ucMonth){
        case 4:
        case 6:
        case 9:
        case 11:
          ucDaysOfMonth = 30;
          break;

        case 2:
          if ((date->ucYear % 4) == 0) ucDaysOfMonth = 29;
          else ucDaysOfMonth = 28;
          break;
      }
      modifier = modifier % ucDaysOfMonth;
      date->ucDay += modifier;
      date->ucDay = date->ucDay % ucDaysOfMonth;
	  if (date->ucDay == 0) date->ucDay = ucDaysOfMonth;
      break;
    }
  }
  return 0;
}

// add two timestamp, if hours > 24 -> return 1 (overflow)
unsigned char timeAdd(t_time *time, t_time *timeAdd){
  unsigned long ulT;

  ulT = time->hour*3600 + time->minute*60 + time->second;
  ulT += timeAdd->hour*3600 + timeAdd->minute*60 + timeAdd->second;

  time->hour = ulT / 3600;
  ulT = ulT - time->hour*3600;
  time->minute = ulT / 60;
  ulT = ulT - time->minute*60;
  time->second = ulT;
  
  if (time->hour >= 24){
    time->hour = time->hour % 24;
    return 1;
  }
  return 0;
}

#undef _TIMEBASE_C_
